#include "CSerialPort.h"


CSerialPort::CSerialPort() : m_hComm(INVALID_HANDLE_VALUE),
m_hEvent(NULL)
{
	m_hKernel32 = GetModuleHandle("KERNEL32.DLL");
	if (m_hKernel32)
		m_lpfnCancelIo = reinterpret_cast<LPCANCELIO>(GetProcAddress(m_hKernel32, "CancelIo"));
	else
		m_lpfnCancelIo = NULL;
}
CSerialPort::~CSerialPort()
{
	Close();
}
void CSerialPort::Open(int nPort, DWORD dwBaud , Parity parity , BYTE DataBits , 
					   StopBits stopBits , FlowControl fc, BOOL bOverlapped )
{
	char sPort[40]="";
	sprintf(sPort,"\\\\.\\COM%d",nPort);
	//sPort.Format(_T("\\\\.\\COM%d"), nPort);

	//Then delegate the work to the other version of Open
	Open(sPort, dwBaud, parity, DataBits, stopBits, fc, bOverlapped);
}
void CSerialPort::Open(LPCTSTR pszPort, DWORD dwBaud , Parity parity , BYTE DataBits , 
		  StopBits stopBits , FlowControl fc , BOOL bOverlapped )
{
	Close(); //In case we are already open

	//Call CreateFile to open the comms port
	m_hComm = CreateFile(pszPort, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, bOverlapped ? FILE_FLAG_OVERLAPPED : 0, NULL);
	if (m_hComm == INVALID_HANDLE_VALUE)
	{
		DWORD dwLastError = GetLastError();
		printf("CSerialPort::Open, Failed to open the comms port, Error:%d\n", dwLastError);
		//ThrowSerialException(dwLastError);
	}

	//Create the event we need for later synchronisation use
	m_hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
	if (m_hEvent == NULL)
	{
		DWORD dwLastError = GetLastError();
		Close();
		printf("CSerialPort::Open, Failed in call to CreateEvent in Open, Error:%d\n", dwLastError);
		//ThrowSerialException(dwLastError);
	}
	DCB dcb;
	dcb.DCBlength = sizeof(DCB);
	GetState(dcb);

	//Setup the baud rate
	dcb.BaudRate = dwBaud; 
	switch (parity)
	{
	case EvenParity:  
		{
			dcb.Parity = EVENPARITY;
			break;
		}
	case MarkParity:  
		{
			dcb.Parity = MARKPARITY;
			break;
		}
	case NoParity:    
		{
			dcb.Parity = NOPARITY;
			break;
		}
	case OddParity:   
		{
			dcb.Parity = ODDPARITY;
			break;
		}
	case SpaceParity: 
		{
			dcb.Parity = SPACEPARITY;
			break;
		}
	default:          
		{
			//ASSERT(FALSE);            
			break;
		}
	}
	dcb.ByteSize = DataBits;

	//Setup the stop bits
	switch (stopBits)
	{
	case OneStopBit:           
		{
			dcb.StopBits = ONESTOPBIT; 
			break;
		}
	case OnePointFiveStopBits: 
		{
			dcb.StopBits = ONE5STOPBITS;
			break;
		}
	case TwoStopBits:          
		{
			dcb.StopBits = TWOSTOPBITS;
			break;
		}
	default:                   
		{
			//ASSERT(FALSE);
			break;
		}
	}
	//Setup the flow control 
	dcb.fDsrSensitivity = FALSE;
	switch (fc)
	{
	case NoFlowControl:
		{
			dcb.fOutxCtsFlow = FALSE;
			dcb.fOutxDsrFlow = FALSE;
			dcb.fOutX = FALSE;
			dcb.fInX = FALSE;
			break;
		}
	case CtsRtsFlowControl:
		{
			dcb.fOutxCtsFlow = TRUE;
			dcb.fOutxDsrFlow = FALSE;
			dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
			dcb.fOutX = FALSE;
			dcb.fInX = FALSE;
			break;
		}
	case CtsDtrFlowControl:
		{
			dcb.fOutxCtsFlow = TRUE;
			dcb.fOutxDsrFlow = FALSE;
			dcb.fDtrControl = DTR_CONTROL_HANDSHAKE;
			dcb.fOutX = FALSE;
			dcb.fInX = FALSE;
			break;
		}
	case DsrRtsFlowControl:
		{
			dcb.fOutxCtsFlow = FALSE;
			dcb.fOutxDsrFlow = TRUE;
			dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
			dcb.fOutX = FALSE;
			dcb.fInX = FALSE;
			break;
		}
	case DsrDtrFlowControl:
		{
			dcb.fOutxCtsFlow = FALSE;
			dcb.fOutxDsrFlow = TRUE;
			dcb.fDtrControl = DTR_CONTROL_HANDSHAKE;
			dcb.fOutX = FALSE;
			dcb.fInX = FALSE;
			break;
		}
	case XonXoffFlowControl:
		{
			dcb.fOutxCtsFlow = FALSE;
			dcb.fOutxDsrFlow = FALSE;
			dcb.fOutX = TRUE;
			dcb.fInX = TRUE;
			dcb.XonChar = 0x11;
			dcb.XoffChar = 0x13;
			dcb.XoffLim = 100;
			dcb.XonLim = 100;
			break;
		}
	default:
		{
			//ASSERT(FALSE);
			break;
		}
	}

	//Now that we have all the settings in place, make the changes
	SetState(dcb);




}


//Reading / Writing Methods
DWORD CSerialPort::Read(void* lpBuf, DWORD dwCount)
{
	
   if(IsOpen())
   {
	DWORD dwBytesRead = 0;
	if (!ReadFile(m_hComm, lpBuf, dwCount, &dwBytesRead, NULL))
	{
		DWORD dwLastError = GetLastError();
		printf("CSerialPort::Read, Failed in call to ReadFile, Error:%d\n", dwLastError);
		//ThrowSerialException(dwLastError);
	}

	return dwBytesRead;
   }
   else 
	   return -1;
}
void  CSerialPort::Read(void* lpBuf, DWORD dwCount, OVERLAPPED& overlapped, DWORD* pBytesRead)
{
	//ASSERT(IsOpen());
    if(IsOpen())
	{
	DWORD dwBytesRead = 0;
	BOOL bSuccess = ReadFile(m_hComm, lpBuf, dwCount, &dwBytesRead, &overlapped);
	if (!bSuccess)
	{
		DWORD dwLastError = GetLastError();
		printf("CSerialPort::Read, Failed in call to ReadFile, Error:%d\n", dwLastError);
		//ThrowSerialException(dwLastError);
	}
	else
	{
		if (pBytesRead)
			*pBytesRead = dwBytesRead;
	}
	}
	

}
void  CSerialPort::ReadEx(void* lpBuf, DWORD dwCount)
{

}
DWORD CSerialPort::Write(const void* lpBuf, DWORD dwCount)
{
	//ASSERT(IsOpen());
    if(IsOpen())
	{
		DWORD dwBytesWritten = 0;
		if (!WriteFile(m_hComm, lpBuf, dwCount, &dwBytesWritten, NULL))
		{
			DWORD dwLastError = GetLastError();
			printf("CSerialPort::Write, Failed in call to WriteFile, Error:%d\n", dwLastError);
			//ThrowSerialException(dwLastError);
		}

		return dwBytesWritten;
	}
	else return -1;

}
void  CSerialPort::Write(const void* lpBuf, DWORD dwCount, OVERLAPPED& overlapped, DWORD* pBytesWritten)
{
	//ASSERT(IsOpen());
    if(IsOpen())
	{

	
	DWORD dwBytesWritten = 0;
	BOOL bSuccess = WriteFile(m_hComm, lpBuf, dwCount, &dwBytesWritten, &overlapped);
	if (!bSuccess)
	{
		DWORD dwLastError = GetLastError();
		printf("CSerialPort::Write, Failed in call to WriteFile, Error:%d\n", dwLastError);
		//ThrowSerialException(dwLastError);
	}
	else
	{
		if (pBytesWritten)
			*pBytesWritten = dwBytesWritten;
	}
	}

}
//void  CSerialPort::WriteEx(const void* lpBuf, DWORD dwCount)
//{
//	//ASSERT(IsOpen());
//    if(!IsOpen())
//		return ;
//	OVERLAPPED* pOverlapped = new OVERLAPPED;
//	memset(pOverlapped, 0, sizeof(OVERLAPPED));
//	pOverlapped->hEvent = static_cast<HANDLE>(this);
//	if (!WriteFileEx(m_hComm, lpBuf, dwCount, pOverlapped, _OnCompletion))
//	{
//		DWORD dwLastError = GetLastError();
//		delete pOverlapped;
//		printf("CSerialPort::WriteEx, Failed in call to WriteFileEx, Error:%d\n", dwLastError);
//		//ThrowSerialException(dwLastError);
//	}
//}
void  CSerialPort::TransmitChar(char cChar)
{
	//ASSERT(IsOpen());
    if(!IsOpen())
		return;
	if (!TransmitCommChar(m_hComm, cChar))
	{
		DWORD dwLastError = GetLastError();
		printf("CSerialPort::TransmitChar, Failed in call to TransmitCommChar, Error:%d\n", dwLastError);
		//ThrowSerialException(dwLastError);
	}

}
void  CSerialPort::GetOverlappedResult(OVERLAPPED& overlapped, DWORD& dwBytesTransferred, BOOL bWait)
{
	//ASSERT(IsOpen());
    if(!IsOpen())
		return ;
	if (!::GetOverlappedResult(m_hComm, &overlapped, &dwBytesTransferred, bWait))
	{
		DWORD dwLastError = GetLastError();
		printf("CSerialPort::GetOverlappedResult, Failed in call to GetOverlappedResult, Error:%d\n", dwLastError);
		//ThrowSerialException(dwLastError);
	}

}
void  CSerialPort::CancelIo()
{
	//ASSERT(IsOpen());
    if(IsOpen())
    return;
	if (m_lpfnCancelIo == NULL)
	{
		printf("CSerialPort::CancelIo, CancelIo function is not supported on this OS. You need to be running at least NT 4 or Win 98 to use this function\n");
		//ThrowSerialException(ERROR_CALL_NOT_IMPLEMENTED);  
	}

	if (!m_lpfnCancelIo(m_hComm))
	{
		DWORD dwLastError = GetLastError();
		printf("Failed in call to CancelIO, Error:%d\n", dwLastError);
		//ThrowSerialException(dwLastError);
	}

}
DWORD CSerialPort::BytesWaiting()
{
	//ASSERT(IsOpen());
    if(IsOpen())
		return -1;
	//Check to see how many characters are unread
	COMSTAT stat;
	GetStatus(stat);
	return stat.cbInQue; 
}
BOOL  CSerialPort::DataWaiting(DWORD dwTimeout)
{
	//Validate our parameters
	//ASSERT(IsOpen());
    if(!IsOpen())
		return FALSE;
	//Setup to wait for incoming data
	DWORD dwOldMask;
	GetMask(dwOldMask);
	SetMask(EV_RXCHAR);

	//Setup the overlapped structure
	OVERLAPPED o;
	o.hEvent = m_hEvent;

	//Assume the worst;
	BOOL bSuccess = FALSE;

	DWORD dwEvent;
	bSuccess = WaitEvent(dwEvent, o);
	if (!bSuccess)
	{
		if (WaitForSingleObject(o.hEvent, dwTimeout) == WAIT_OBJECT_0)
		{
			DWORD dwBytesTransferred;
			GetOverlappedResult(o, dwBytesTransferred, FALSE);
			bSuccess = TRUE;
		}
	}

	//Reset the event mask
	SetMask(dwOldMask);

	return bSuccess;

}

//Configuration Methods
void CSerialPort::GetConfig(COMMCONFIG& config)
{

}
 void CSerialPort::GetDefaultConfig(int nPort, COMMCONFIG& config)
{

}
void CSerialPort::SetConfig(COMMCONFIG& Config)
{

}
 void CSerialPort::SetDefaultConfig(int nPort, COMMCONFIG& config)
{

}
void CSerialPort::GetStatus(COMSTAT& stat)
{
	//Validate our parameters
	//ASSERT(IsOpen());
    if(!IsOpen())
		return;
	DWORD dwErrors;
	if (!ClearCommError(m_hComm, &dwErrors, &stat))
	{
		DWORD dwLastError = GetLastError();
		printf("CSerialPort::GetStatus, Failed in call to ClearCommError, Error:%d\n", dwLastError);
		//ThrowSerialException(dwLastError);
	}
}
void CSerialPort::GetState(DCB& dcb)
{
	//Validate our parameters
	//ASSERT(IsOpen());

	if (!GetCommState(m_hComm, &dcb))
	{
		DWORD dwLastError = GetLastError();
		printf("CSerialPort::GetState, Failed in call to GetCommState, Error:%d\n", dwLastError);
		//ThrowSerialException(dwLastError);
	}
}
void CSerialPort::SetState(DCB& dcb)
{
	//Validate our parameters
	//ASSERT(IsOpen());

	if (!SetCommState(m_hComm, &dcb))
	{
		DWORD dwLastError = GetLastError();
		printf("CSerialPort::SetState, Failed in call to SetCommState, Error:%d\n", dwLastError);
		//ThrowSerialException(dwLastError);
	}
}
void CSerialPort::GetMask(DWORD& dwMask)
{
	//Validate our parameters
	//ASSERT(IsOpen());
    if(IsOpen())
		return;
	if (!GetCommMask(m_hComm, &dwMask))
	{
		DWORD dwLastError = GetLastError();
		printf("CSerialPort::GetMask, Failed in call to GetCommMask, Error:%d\n", dwLastError);
		//ThrowSerialException(dwLastError);
	}
}
void CSerialPort::WaitEvent(DWORD& dwMask)
{
	//Validate our parameters
	//ASSERT(IsOpen());
    if(!IsOpen())
		return ;
	if (!WaitCommEvent(m_hComm, &dwMask, NULL))
	{
		DWORD dwLastError = GetLastError();
		printf("CSerialPort::WaitEvent, Failed in call to WaitCommEvent, Error:%d\n", dwLastError);
		//ThrowSerialException(dwLastError);
	}
}

BOOL CSerialPort::WaitEvent(DWORD& dwMask, OVERLAPPED& overlapped)
{
	//Validate our parameters
	//ASSERT(IsOpen());
    if(IsOpen())
		return FALSE;
	//ASSERT(overlapped.hEvent);
    if(overlapped.hEvent)
		return FALSE;
	BOOL bSuccess = WaitCommEvent(m_hComm, &dwMask, &overlapped);
	if (!bSuccess)
	{
		DWORD dwLastError = GetLastError();
		printf("CSerialPort::WaitEvent, Failed in call to WaitCommEvent, Error:%d\n", dwLastError);
		//ThrowSerialException(dwLastError);
	}

	return bSuccess;
}
void CSerialPort::SetMask(DWORD dwMask)
{
	//Validate our parameters
	//ASSERT(IsOpen());
    if(IsOpen())
		return ;
	if (!SetCommMask(m_hComm, dwMask))
	{
		DWORD dwLastError = GetLastError();
		printf("CSerialPort::SetMask, Failed in call to SetCommMask, Error:%d\n", dwLastError);
		//ThrowSerialException(dwLastError);
	}
}

void CSerialPort::SetTimeouts(COMMTIMEOUTS& timeouts)
{
	//ASSERT(IsOpen());
    if(IsOpen())
		return ;
	if (!SetCommTimeouts(m_hComm, &timeouts))
	{
		DWORD dwLastError = GetLastError();
		printf("CSerialPort::SetTimeouts, Failed in call to SetCommTimeouts, Error:%d\n", dwLastError);
		//ThrowSerialException(dwLastError);
	}
}
void CSerialPort::GetTimeouts(COMMTIMEOUTS& timeouts)
{
	//ASSERT(IsOpen());
    if(IsOpen())
		return ;
	if (!GetCommTimeouts(m_hComm, &timeouts))
	{
		DWORD dwLastError = GetLastError();
		printf("CSerialPort::GetTimeouts, Failed in call to GetCommTimeouts, Error:%d\n", dwLastError);
		//ThrowSerialException(dwLastError);
	}
}
void CSerialPort::Set0Timeout()
{
	COMMTIMEOUTS Timeouts;
	memset(&Timeouts, 0, sizeof(Timeouts));
	Timeouts.ReadIntervalTimeout = MAXDWORD;
	SetTimeouts(Timeouts);
}
void CSerialPort::Set0WriteTimeout()
{
	COMMTIMEOUTS Timeouts;
	GetTimeouts(Timeouts);
	Timeouts.WriteTotalTimeoutMultiplier = 0;
	Timeouts.WriteTotalTimeoutConstant = 0;
	SetTimeouts(Timeouts);
}
void CSerialPort::Set0ReadTimeout()
{

	COMMTIMEOUTS Timeouts;
	GetTimeouts(Timeouts);
	Timeouts.ReadIntervalTimeout = MAXDWORD;
	Timeouts.ReadTotalTimeoutMultiplier = 0;
	Timeouts.ReadTotalTimeoutConstant = 0;
	SetTimeouts(Timeouts);
}

//CSerialPort * GetSerialPort()
//{
//	return new CSerialPort();
//}